package org.frame.auth.token;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import javax.servlet.http.HttpServletRequest;

import org.frame.auth.common.utils.ServletUtils;
import org.frame.common.core.AddressUtils;
import org.frame.common.core.IdUtils;
import org.frame.common.core.IpUtils;
import org.frame.common.core.StringUtils;
import org.frame.common.core.constants.Constants;
import org.frame.common.core.entity.TokenUser;
import org.frame.common.redis.RedisCache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import eu.bitwalker.useragentutils.UserAgent;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtParser;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;

/**
 * token验证处理
 * 
 * @author ty-intasect03
 *
 */
@Slf4j
@Component
public class TokenService {

	protected static final long MILLIS_SECOND = 1000;
	protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND;

	@Autowired
	private RedisCache redisCache;

	/**
	 * 获取用户身份信息
	 * 
	 * @return 用户信息
	 */
	public TokenUser getTokenUser(HttpServletRequest request) {
		// 获取请求携带的令牌
		String token = getToken(request);
		if (StringUtils.isNotEmpty(token)) {
			String secret = request.getHeader("secret");
			log.debug("secret:{}", secret);
			JwtParser jwtParser = Jwts.parser().setSigningKey(secret);
			Claims claims = jwtParser.parseClaimsJws(token).getBody();
			// 解析对应的权限以及用户信息
			String uuid = (String) claims.get(Constants.LOGIN_USER_KEY);
			String userKey = getTokenKey(uuid);
			TokenUser user = redisCache.getCacheObject(userKey);
			return user;
		}
		return null;
	}

	/**
	 * 获取请求token
	 *
	 * @param request
	 * @return token
	 */
	private String getToken(HttpServletRequest request) {
		String tokenHaeder = request.getHeader("header");
		String token = request.getHeader(tokenHaeder);
		log.debug("token:{}", token);
		if (StringUtils.isNotEmpty(token) && token.startsWith(Constants.TOKEN_PREFIX)) {
			token = token.replace(Constants.TOKEN_PREFIX, "");
		}
		return token;
	}

	/**
	 * 创建令牌
	 * 
	 * @param TokenUser 用户信息
	 * @return 令牌
	 */
	public String createToken(TokenUser TokenUser) {
		HttpServletRequest request = ServletUtils.getRequest();
		String token = IdUtils.fastUUID();
		TokenUser.setToken(token);
		setUserAgent(TokenUser);
		refreshToken(TokenUser, request);

		Map<String, Object> claims = new HashMap<>();
		claims.put(Constants.LOGIN_USER_KEY, token);
		return createToken(claims, request);
	}

	/**
	 * 刷新令牌有效期
	 * 
	 * @param TokenUser 登录信息
	 */
	public void refreshToken(TokenUser TokenUser, HttpServletRequest request) {
		String expireTimeStr = request.getHeader("expireTime");
		log.debug("expireTimeStr:{}", expireTimeStr);
		int expireTime = StringUtils.isNumeric(expireTimeStr) ? Integer.valueOf(expireTimeStr) : 30;
		TokenUser.setLoginTime(System.currentTimeMillis());
		TokenUser.setExpireTime(TokenUser.getLoginTime() + expireTime * MILLIS_MINUTE);
		// 根据uuid将TokenUser缓存
		String userKey = getTokenKey(TokenUser.getToken());
		redisCache.setCacheObject(userKey, TokenUser, expireTime, TimeUnit.MINUTES);
	}

	/**
	 * 设置用户代理信息
	 * 
	 * @param TokenUser 登录信息
	 */
	public void setUserAgent(TokenUser TokenUser) {
		UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent"));
		String ip = IpUtils.getIpAddr(ServletUtils.getRequest());
		TokenUser.setIpaddr(ip);
		TokenUser.setLoginLocation(AddressUtils.getRealAddressByIP(ip));
		TokenUser.setBrowser(userAgent.getBrowser().getName());
		TokenUser.setOs(userAgent.getOperatingSystem().getName());
	}

	/**
	 * 从数据声明生成令牌
	 *
	 * @param claims 数据声明
	 * @return 令牌
	 */
	private String createToken(Map<String, Object> claims, HttpServletRequest request) {
		String secret = request.getHeader("secret");
		log.debug("secret:{}", secret);
		return Jwts.builder().setClaims(claims).signWith(SignatureAlgorithm.HS512, secret).compact();
	}

	private String getTokenKey(String uuid) {
		return Constants.LOGIN_TOKEN_KEY + uuid;
	}

}
